<?php
class SQL{
	private static $connection;
	private static $committed;
	protected static $has_error;
	protected static $query_shown=false;
	
	protected static $counter=0;
	protected static $counter_cached=false;
	protected static $query_caches=array();
	
	protected static $start_time;
	
	
	/**
	 * @desc Make a connection to SQL server.
	 */
	public static function init(){
		self::$connection=&mysqli_connect(DB_SERVER,DB_USERNAME,DB_PASSWORD,DB_DBNAME);
		self::$connection->query('set names latin1');
		if (mysqli_connect_errno()){
		   throw new Exception("Connection failed.\n");
		}
		self::$start_time=microtime();
	}
	
	
	
	/**
	 * @desc Check that the connection is fine.
	 */
	public static function ok(){
		if (empty(self::$connection)){
//			self::error("Empty connection.\n");
			throw new Exception("Empty connection");
		}
	}
	
	
	
	/**
	 * @desc Close the connection.
	 */
	public static function close(){
		mysqli_close(self::$connection);
	}

	
	
	/**
	 * @desc Roll back the result on error.
	 */
	public static function rollback(){
		mysqli_rollback(self::$connection);  // if error, roll back transaction
	}

	
	
	/**
	 * @desc Rollback with error cached.
	 * @param string $message The message error.
	 */
	public static function undone(&$message=""){
		if (!self::$committed){
			return true;
		}
		mysqli_rollback(self::$connection);
		if (!empty($message)){
			self::error($message);
			return false;
		}
		return true;
	}

	
	
	/**
	 * @desc Start a transaction. Use finish()
	 * to finish the transaction.
	 */
	public static function commit(){
		//begin transaction
		$result = mysqli_autocommit(self::$connection, FALSE);
		if (!$result) {
			fatal("commit");
		   return false;
		}
		self::$committed=true;
		return true;
	}

	
	
	/**
	 * @desc Finish the connection.
	 */
	public static function finish(){
		if (!self::$committed){
			return true;
		}
		
		//when finishing, need to commit the transaction
		$result = mysqli_commit(self::$connection);
		if (!$result) {
		   self::rollback(self::$connection, "commit");  // if error, roll back transaction
		   return false;
		}
		return true;
	}
	
	
	
	/**
	 * @desc Trigger a flag to indicate that we will cache the number of query.
	 * @param Boolean $flag
	 */
	public static function cache($flag=true){
		self::$counter_cached=$flag;
	}
	
	
	/**
	 * @desc Joob_Utils_Log the number of request to a file.
	 */
	public static function logCounter(){
		if (!self::$counter_cached){
			return;
		}
		$ip=Utils::ip();
		$counter=self::$counter;
		$time=date("Y-m-d H:i:s");
		$filename="dblogs/".date("Y-m-d").".log";
		$queries="";
		for ($i=0; $i<count(self::$query_caches); $i++){
			$queries.="\t".self::$query_caches[$i]."\n";
		}
		$second=microtime()-self::$start_time;
		Joob_Utils_Log::write("[$time @$ip] $counter queries in $second seconds.\n$queries",$filename);
	}
	
	
	/**
	 * @desc Setting flag to indicate that the query will be exposed.
	 */
	public static function showQuery($flag=true){
		self::$query_shown=$flag;
	}
	
	
	/**
         * @desc Mark that the table encounters error and and undone
         * action should be made.
         * 
         * @param String $str 
         */
        public static function markError(&$str=''){
        self::$has_error=true;
        }
        
        
	/**
         * @desc Clear database error
         * 
         * @param String $str 
         */
        public static function clearError(){
        self::$has_error=false;
        }
        
        
	/**
         * @desc Clear database error
         * 
         * @param String $str 
         */
        public static function hasError(){
        return self::$has_error;
        }
        
        
	
	
	/**
	 * @desc Fetch SQL results into array.
	 * @param SQL data $result
	 */
	public static function fetch(&$result){
		return mysqli_fetch_array($result,MYSQLI_ASSOC);
	}
	
	
	/**
	 * @desc Make a read query. This doesn't trigger any write or update.
	 * @param string $query
	 */
	public static function read($query){
		$result=self::query($query);
		if (!$result || self::isEmpty($result)) return NULL;
		return $result;
	}
	
	
	
	/**
	 * @desc Make a write query. This may trigger an action to maintain
	 * the eventually consistant property. Slavery table may not be writed
	 * immediately, but is appended for later use.
	 */
	public static function write(&$query){
		$result=self::query($query);
		if (!$result){	
			self::error($query);
			return false;
		}
		return $result;
	}
	
	
	/**
	 * @desc Making update query (a write query).
	 * @param string $query
	 */
	public static function update(&$query){
		$result=self::query($query);
		if (!$result){	
			self::error($query);
			return false;
		}
		return true;
	}
	
	
	
	/**
	 * @desc Make a search query.
	 * @param string $query
	 * @alias of read. 
	 */
	public static function get($query){
		return self::read($query);
	}

	
	
	/**
	 * @desc A special case of read, when we only want to get the
	 * first row result. This is very handy if we want to connect
	 * and get result of a single identifier (like, by user_id, get
	 * the user).
	 * @param string $query
	 */
	public static function single($query){
		$result=self::read($query);
		if (!$result || self::isEmpty($result)){
			return NULL;
		}
		$row=mysqli_fetch_array($result,MYSQLI_ASSOC);
		return $row;
	}

	
	/**
	 * @desc Make an insert query.
	 * @param string $query
	 * @return The id of the last insertion.
	 */
	public static function insert($query){
		$row=self::write($query);
		if ($row!==false){
			return self::lastInsertion();
		}
		else return -1;	
	}


	/**
	 * Changing SQL results to array.
	 * @param unknown_type $result
	 */
	public static function to_array(&$result){
		$a=array();
		if ($result==null){
			return $a;
		}
		while ($row=mysqli_fetch_array($result,MYSQLI_ASSOC)){
			$a[]=$row;
		}
		return $a;
	}	

	
	/**
	 * 
	 * @desc Changing SQL result to an array of objects.
	 * @param SQL data $result
	 * @param string $obj_name The name of the object.
	 */
	public static function to_obj(&$result,$obj_name){
		$a=array();
		if ($result==null || count($result)<=0){
			return $a;
		}
		while ($row=mysqli_fetch_array($result,MYSQLI_ASSOC)){
			$obj=new $obj_name();
			$obj->extend($row);
			$a[]=$obj;
		}
		return $a;
	}
	
	
	
	/**
	 * ERROR HANDLING
	 */
	
	/**
	 * Showing the error.
	 * @param string $error.
	 */
	public static function error($error){
		error($error);
	}
	
	
	/**
	 * Show a fatal error
	 * @param string $error
	 */
	public static function fatal($error){
		fatal($error);
	}
	
	
		
	/**
	 * List of alias function
	 */

	/**
	 * @desc Alias of get()
	 * @param unknown_type $query
	 */
	public static function search(&$query){
		return self::get($query);
	}
	
	
	/**
	 * @desc Alias of finish()
	 */
	public static function done(){
		return self::finish();
	}
	

	/**
	 * @desc Alias of ok()
	 */
	public static function connected(){
		return self::ok();
	}
	
	
	/**
	 * @desc Alias of to_array
	 * @param SQL data $result
	 */
	public static function toArray(&$result){
		return self::to_array($result);
	}
	
	# Alias of to_obj()
	public static function toObj(&$result,$obj_name){
		return self::to_obj($result,$obj_name);
	}
        
        
	/**
	 * @desc Check if the result returned by SQL result is empty.
	 * @param SQL data $result
	 */
	private static function isEmpty(&$result){
		if (mysqli_num_rows($result)==0){
			return true;
		}
		return false;
	}
	
	/**
	* @desc Making the query.
	* @param SQL query $query
	*/
	protected static function query($query){ 
		if (self::$query_shown){
			echo $query;
		}
		
		self::$counter++;
		self::$query_caches[]=$query;
		
		return mysqli_query(self::$connection,$query);
	}
	
	
        /**
         * @desc Return the last insertion of the database.
         */
        private static function lastInsertion(){
            $query="select Last_Insert_ID() as last_id";
            $result=self::single($query);
            if ($result==NULL) return -1;
            return $result["last_id"];
        }
        
        
}

SQL::init();
?>